<?php

/**
 * Maikuro Framework
 *
 * LICENSE:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * The license is bundled with this package in the file LICENSE.txt.
 * You may also obtain a copy of the License at this URL:
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @copyright  Copyright (c) 2010, Jonathan Hedrén
 * @author     Jonathan Hedrén <jonathan.hedren@gmail.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */

/**
 * "Controller, View and Response in one"
 */
class View
{
    const SESSION_MESSENGER_KEY = 'session_messenger_key';
    
    /**
     * Flag to determine whether to gzip encode the response body or not.
     *
     * @var boolean
     */
    protected $gzipEncoding = false;

    /**
     * The response headers.
     *
     * @var array
     */
    protected $headers = array();

    /**
     * The name of the layout to wrap the view in.
     *
     * @var string
     */
    protected $layout;

    /**
     * The name of the current module that the view belongs to.
     *
     * @var string
     */
    protected $module;

    /**
     * The name of the view.
     *
     * @var string
     */
    protected $name;

    /**
     * The output of the parsed view file.
     *
     * @var string
     */
    protected $output;

    /**
     * The request.
     *
     * @var Request
     */
    protected $request;

    /**
     * An associative array of variable names and values is accessible directly
     * within the view (or layout).
     *
     * @var array
     */
    protected $vars = array();

    /**
     * Constructor.
     *
     * @param Request $request
     * @return View
     */
    public function __construct(Request $request) {
        $this->request = $request;
        return $this;
    }

    /**
     * Does the actual inclusion of any view or layout file. Sets up the
     * Request and the key-value-pairs in $this->vars as variables in the scope.
     *
     * @param string $file
     * @return View
     */
    protected function doParse($file)
    {
        if (!file_exists($file)) {
            throw new Exception('No file found @ ' . $file);
        }

        $request = $this->request;

        foreach ($this->vars as $var => $value) {
            ${$var} = $value;
        }

        ob_start();
        include($file);
        $output = ob_get_contents();
        ob_end_clean();

        $this->output = $output;

        return $this;
    }

    /**
     * Returns a Form. If a form class is specified, the specified form will be
     * created and returned, else an empty form (of class Form) will be
     * returned.
     *
     * @param string $formClass
     * @return Form
     */
    protected function getForm($formClass = null)
    {
        require_once 'Form.php';
        if ($formClass !== null) {
            require_once 'src/forms/' . $formClass . '.php';
            return new $formClass();
        }
        return new Form();
    }

    /**
     * Returns an array of headers.
     *
     * @return array
     */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
     * Returns a Messenger which is used to store "flash" messages in the
     * session.
     *
     * @return Messenger
     */
    public function getMessenger()
    {
        $messenger = Session::get(self::SESSION_MESSENGER_KEY);
        if ($messenger === null) {
            $messenger = new Messenger();
            Session::set(self::SESSION_MESSENGER_KEY, $messenger);
        }
        return $messenger;
    }

    /**
     * Returns the name of the module (if any).
     *
     * @return string
     */
    public function getModule()
    {
        return $this->module;
    }

    /**
     * Returns the name of the view.
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Returns the output of the parsed file.
     *
     * @return string
     */
    public function getOutput()
    {
        return $this->output;
    }

    /**
     * Returns the $this->vars array.
     *
     * @return array
     */
    public function getVars()
    {
        return $this->vars;
    }

    /**
     * Parses the view by parsing the view file and the optional layout file.
     *
     * @param string $view
     * @return string
     */
    public function parse()
    {
        if ($this->name === null || $this->name === '') {
            throw new Exception('Can not parse an unknown view.');
        }

        if ($this->module !== null) {
            $viewFile = 'src/views/' . $this->module . '/' . $this->name . '.php';
        } else {
            $viewFile = 'src/views/' . $this->name . '.php';
        }
        
        $this->doParse($viewFile);

        if ($this->layout !== null) {
            $this->doParse('src/layouts/' . $this->layout . '.php');
        }

        return $this;
    }

    /**
     * Renders a snippet. 
     *
     * @param string $snippetName
     * @param array $vars
     */
    protected function renderSnippet($snippetName, array $vars = array())
    {
        $snippetFile = 'src/snippets/' . $snippetName . '.php';

        $viewVars = $this->vars;

        $this->vars = array_merge($this->vars, $vars);

        $this->doParse($snippetFile);
        
        $this->vars = $viewVars;

        echo $this->getOutput();
    }

    /**
     * Sets an HTTP header.
     *
     * @param string $header
     * @return View
     */
    public function setHeader($header, $headerValue)
    {
        $this->headers[$header] = $headerValue;
        return $this;
    }

    /**
     * Sets the HTTP status.
     *
     * @param string $httpStatus
     * @param integer $responseCode
     * @return View
     */
    public function setHttpStatus($httpStatus, $responseCode)
    {
        header($httpStatus, true, $responseCode);
        $this->setHeader('Status', $responseCode);
        return $this;
    }

    /**
     *
     * @param string $layout
     * @return View
     */
    public function setLayout($layout)
    {
        $this->layout = $layout;
        return $this;
    }

    /**
     * Sets the module name.
     *
     * @param string $module
     * @return View
     */
    public function setModule($module)
    {
        $this->module = $module;
        return $this;
    }

    /**
     * Sets the name of the view.
     *
     * @param string $name
     * @return View
     */
    public function setName($name)
    {
        $this->name = $name;
        return $this;
    }

    /**
     *
     * @param string $key
     * @param mixed $value
     * @return View
     */
    public function setVar($key, $value)
    {
        $this->vars[$key] = $value;
        return $this;
    }

    /**
     *
     * @param array $vars
     * @return View
     */
    public function setVars(array $vars)
    {
        $this->vars = array_merge($this->vars, $vars);
        return $this;
    }

    /**
     * Sets whether to gzip encode the response body or not.
     *
     * @param boolean $gzipEncoding
     * @return View
     */
    public function useGzipEncoding($gzipEncoding = null)
    {
        if ($gzipEncoding === null) {
            return $this->gzipEncoding;
        }
        $this->gzipEncoding = $gzipEncoding;
        return $this;
    }
}